#pragma once

#include <curl/curl.h>
#include <stdexcept>
#include <cstddef>

namespace curlwork {

typedef CURL* easy_handle_type;

typedef CURLM* multi_handle_type;

typedef CURLcode error_type;

typedef CURLMcode merror_type;

typedef CURLoption option_type;

typedef CURLINFO info_type;

typedef CURLMsg msg_type;

typedef curl_off_t offset_type;

static const int no_error = CURLE_OK;

static inline const char* description(error_type err)
{
    return curl_easy_strerror(err);
}

static inline const char* description(merror_type err)
{
    return curl_multi_strerror(err);
}

template<typename T>
static void set_option(easy_handle_type h, option_type o, const T& t)
{
    error_type e = curl_easy_setopt(h, o, t);

    if (e != no_error)
        throw std::runtime_error(description(e));
}

class delegate
{
    typedef void (*complete_type)(void *object, error_type error);

    void *object_;
    complete_type func_;

public:

    delegate(void *object, complete_type f)
        : object_(object)
        , func_(f)
    {   }

    void apply(error_type error) const
    {
        func_(object_, error);
    }
};

struct pause
{
    enum type
    {
        recv = CURLPAUSE_RECV,
        send = CURLPAUSE_SEND,
        all = CURLPAUSE_ALL,
    };
};

struct seek
{
    enum position
    {
        set = SEEK_SET,
        cur = SEEK_CUR,
        end = SEEK_END
    };

    enum result
    {
        ok = CURL_SEEKFUNC_OK,
        fail = CURL_SEEKFUNC_FAIL,
        cant_seek = CURL_SEEKFUNC_CANTSEEK
    };
};
// you need to create one static global object in windows
struct global
{
    global() throw()
    {
        /* In windows, this will init the winsock stuff */
        curl_global_init(CURL_GLOBAL_ALL);
    }

    ~global() throw()
    {
        curl_global_cleanup();
    }
};

} // namespace curlwork
